package com.xianyu.open.restclient.utils;

import com.alibaba.fastjson.JSON;
import com.xianyu.open.restclient.config.RestClientProperties;
import com.xianyu.open.restclient.constants.RestClientApplicationContextUtils;
import com.xianyu.open.restclient.exception.RestClientException;
import com.xianyu.open.restclient.exception.RestClientExceptionModel;
import org.apache.http.client.HttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import java.nio.charset.Charset;
import java.util.List;
import java.util.Map;

/**
 *
 * @author bailinan
 * @date 2018/6/18
 */
public class RestTemplateUtil {
    private static RestTemplate restTemplate;

    static {
        HttpComponentsClientHttpRequestFactory httpRequestFactory = new HttpComponentsClientHttpRequestFactory();
        HttpClient httpClient = HttpClientBuilder.create().disableCookieManagement().disableRedirectHandling().build();
        httpRequestFactory.setHttpClient(httpClient);

        // 外部配置设置超时
        RestClientProperties restClientProperties = RestClientApplicationContextUtils.getBean(RestClientProperties.class);
        httpRequestFactory.setConnectTimeout(restClientProperties.getConnectTimeout());
        httpRequestFactory.setReadTimeout(restClientProperties.getReadTimeout());
        restTemplate = new RestTemplate(httpRequestFactory);
        List<HttpMessageConverter<?>> messageConverters = restTemplate.getMessageConverters();
        for (int i=0;i<messageConverters.size();i++){
            if (messageConverters.get(i) instanceof StringHttpMessageConverter){
                messageConverters.set(i,new StringHttpMessageConverter(Charset.forName("utf-8")));
                break;
            }
        }

    }

    /**
     * 只获取响应体数据
     * @param url
     * @param httpMethod
     * @param params
     * @param headers
     * @param responseType
     * @param uriVariables
     * @param <T>
     * @return
     */
    public static <T> T doRest(String url, HttpMethod httpMethod, Object params,Map<String, String> headers,
                               Class<T> responseType,Object... uriVariables){
        ResponseEntity<T> result = service(url, httpMethod, params, headers, responseType, uriVariables);
        return result.getBody();
    }

    /**
     * 返回完全响应
     * @param url
     * @param httpMethod
     * @param params
     * @param headers
     * @param responseType
     * @param uriVariables
     * @param <T>
     * @return
     */
    public static <T> ResponseEntity<T> doRestForEntity(String url, HttpMethod httpMethod, Object params, Map<String, String> headers,
                                                        Class<T> responseType, Object[] uriVariables) {
        return service(url, httpMethod, params, headers, responseType, uriVariables);
    }

    /**
     * 判断调用
     * @param url
     * @param httpMethod
     * @param params
     * @param headers
     * @param responseType
     * @param uriVariables
     * @param <T>
     * @return
     */
    private static <T> ResponseEntity<T> service(String url, HttpMethod httpMethod, Object params, Map<String, String> headers,
                                                           Class<T> responseType, Object[] uriVariables) {
        ResponseEntity<T> result;
        switch (httpMethod){
            case GET:
                result = doGet(url, httpMethod, headers, responseType, uriVariables);
                break;
            default:
                result = doPost(url, httpMethod, params, headers, responseType, uriVariables);
        }
        return result;
    }

    /**
     * 发出get请求
     * @param url
     * @param httpMethod
     * @param headers
     * @param responseType
     * @param uriVariables
     * @param <T>
     * @return
     */
    private static <T> ResponseEntity<T> doGet(String url, HttpMethod httpMethod, Map<String, String> headers,
                              Class<T> responseType,Object... uriVariables){
        HttpEntity httpEntity = getHttpEntity(headers);
        return getResponseEntity(url, httpMethod, responseType, httpEntity, uriVariables);
    }

    /**
     * 发出废get请求  post、put、delete等
     * @param url
     * @param httpMethod
     * @param params
     * @param headers
     * @param responseType
     * @param uriVariables
     * @param <T>
     * @return
     */
    private static <T> ResponseEntity<T> doPost(String url, HttpMethod httpMethod, Object params,Map<String, String> headers,
                               Class<T> responseType,Object... uriVariables){
        HttpEntity httpEntity = getHttpEntity(params, headers);
        return getResponseEntity(url, httpMethod, responseType, httpEntity, uriVariables);
    }

    /**
     * 请求发出出口
     * @param url
     * @param httpMethod
     * @param responseType
     * @param httpEntity
     * @param uriVariables
     * @param <T>
     * @return
     */
    private static <T> ResponseEntity<T> getResponseEntity(String url, HttpMethod httpMethod, Class<T> responseType,
                                                           HttpEntity httpEntity, Object[] uriVariables) {
        ResponseEntity<T> tResponseEntity;
        ResponseEntity<String> result1 = null;
        try {
            result1 = restTemplate.exchange(url, httpMethod, httpEntity, String.class, uriVariables);
            if (result1.getStatusCode() == HttpStatus.INTERNAL_SERVER_ERROR){
                throw new RuntimeException();
            }
            tResponseEntity = new ResponseEntity<>(JSON.parseObject(result1.getBody(), responseType),
                    result1.getHeaders(), result1.getStatusCode());
        }catch (Exception e){
            RestClientExceptionModel restClientExceptionModel = new RestClientExceptionModel();
            restClientExceptionModel.setUrl(url);
            restClientExceptionModel.setResponseEntity(result1);
            restClientExceptionModel.setRelException(e);
            throw new RestClientException(restClientExceptionModel);
        }

        return tResponseEntity;
    }


    private static HttpEntity getHttpEntity(Map<String, String> headers) {
        MultiValueMap<String, String> relHeaders = new LinkedMultiValueMap<>();
        headers.forEach(relHeaders::add);
        return new HttpEntity(relHeaders);
    }

    @SuppressWarnings("unchecked")
    private static HttpEntity getHttpEntity(Object params, Map<String, String> headers) {
        MultiValueMap<String, String> relHeaders = new LinkedMultiValueMap<>();
        headers.forEach(relHeaders::add);
        if (!"application/json".equals(headers.get("Content-Type"))){
            Map<String, Object> paramsToMap = (Map) params;
            MultiValueMap<String, Object> relParams = new LinkedMultiValueMap<>();
            paramsToMap.forEach(relParams::add);
            return new HttpEntity(relParams,relHeaders);
        }else {
            return new HttpEntity(params,relHeaders);
        }

    }


}
